import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;

/**
 * 自定义锁类，实现了一个简单的独占锁（支持重入）
 */
public class MyLock {

    // 线程安全的状态值，0表示未加锁，大于0表示已加锁及重入次数---------AQS中状态值state，实现可重入锁，使用AtomicInteger类型
    private final AtomicInteger state = new AtomicInteger(0);

    // 记录当前持有锁的线程-----为了避免不是持有锁的线程进行解锁操作
    private Thread owner = null;

    // 线程安全的头节点，用于构建等待队列---当下是多线程环境，使用AtomicReference实现
    private final AtomicReference<Node> head = new AtomicReference<>(new Node());

    // 线程安全的尾节点，用于构建等待队列
    private final AtomicReference<Node> tail = new AtomicReference<>(head.get());

    /**
     * 获取锁的方法
     * 1. 如果锁未被占用，直接获取锁。
     * 2. 如果锁已被当前线程占用，支持重入并增加计数。
     * 3. 如果锁被其他线程占用，将当前线程加入等待队列，并挂起线程直到被唤醒。
     */
    void lock() {
        // 尝试直接获取锁
        if (this.state.get() == 0) {
            //compareAndSet----CAS
            if (this.state.compareAndSet(0, 1)) {
                this.owner = Thread.currentThread();
                System.out.println(Thread.currentThread().getName() + " 直接获取锁");
                return;
            }
        } else {
            // 支持重入锁逻辑
            if (this.owner == Thread.currentThread()) {
                System.out.println(Thread.currentThread().getName() + " 拿到了重入锁,当前重入次数为" + this.state.incrementAndGet());
                return;
            }
        }

        // 当前线程未能获取锁，创建节点并加入等待队列尾部
        Node current = new Node();
        current.thread = Thread.currentThread();

        while (true) {
            //失败后，获取当前最新尾节点
            Node currentTail = this.tail.get();
            if (this.tail.compareAndSet(currentTail, current)) {
                System.out.println(Thread.currentThread().getName() + " 加入了链表尾部");
                currentTail.next = current;
                current.prev = currentTail;
                break;
            }
        }

        // 当前线程进入等待状态，直到成为队列头部并成功获取锁
        while (true) {
            if (current.prev == this.head.get() && this.state.compareAndSet(0, 1)) {
                this.owner = Thread.currentThread();
                this.head.set(current);
                current.prev.next = null;
                current.prev = null;
                System.out.println(Thread.currentThread().getName() + " 被唤醒之后获取锁");
                return;
            }
            // 阻塞线程----这里有个点，在阻塞前先抢锁那么会和被唤醒线程抢锁，就不公平了，非公平锁
            //处理方法实现公平锁，就是不允许线程刚进入就抢锁，必须创建节点加入，等他成为头节点才能运行
            LockSupport.park();
        }
    }

    /**
     * 释放锁的方法
     * 1. 验证当前线程是否持有锁。
     * 2. 如果是重入锁，减少计数并不真正释放锁。
     * 3. 如果计数为0，清空锁状态并唤醒等待队列中的下一个线程。
     */
    void unlock() {
        // 验证当前线程是否持有锁
        if (Thread.currentThread() != this.owner) {
            throw new IllegalStateException("当前线程并没有锁，不能解锁" + Thread.currentThread().getName() + ",owner: " + owner.getName());
        }

        int currentState = this.state.get();

        // 重入锁逻辑：减少计数但不释放锁
        if (currentState > 1) {
            this.state.set(currentState - 1);
            return;
        }

        // 确保解锁时状态合法
        if (currentState <= 0) {
            throw new IllegalStateException("重入锁解锁错误！");
        }

        // 清空锁状态并唤醒等待队列中的下一个线程
        Node headNode = this.head.get();
        Node next = headNode.next;
        this.owner = null;
        this.state.set(0);
        if (next != null) {
            System.out.println(Thread.currentThread().getName() + "唤醒了" + next.thread.getName());
            // 唤醒下一个线程
            LockSupport.unpark(next.thread);
        }
    }

    /**
     * 节点类，用于构建线程等待队列
     */
    static class Node {
        Node prev;
        Node next;
        Thread thread;
    }
}
